﻿using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TopVidionApplication;
using TopVisionLog;
using TopVisionVideoStream;
using VideoStream.Model;
using VideoStream.Windows;

namespace VideoStream.Common
{
    public static class CommonFun
    {
        public static DateTime DT1970 = new DateTime(1970, 1, 1);
        public static string userInfoFileName = "tv.data";
        public static string userInfoFilePath => Path.Combine(Application.StartupPath, userInfoFileName);
        public static string EnDeKey = "!TV2018!";
        public static bool IsHostAlive = false;
        public static string Host => TVVS.Host;

        // Assemblys
        private static string assemblyFormat = "标题:{0}\r\n版本:{1}\r\n描述:{2}\r\n产品:{3}\r\n版权:{4}\r\n公司:{5}\r\n";
        public static string AssemblyTitle => Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyTitleAttribute>()?.FirstOrDefault().Title;
        public static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
        public static string AssemblyDescription => Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyDescriptionAttribute>()?.FirstOrDefault().Description;
        public static string AssemblyProduct => Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyProductAttribute>()?.FirstOrDefault().Product;
        public static string AssemblyCopyright => Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyCopyrightAttribute>()?.FirstOrDefault().Copyright;
        public static string AssemblyCompany => Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyCompanyAttribute>()?.FirstOrDefault().Company;
        public static string ApplicationDescribe => string.Format(assemblyFormat,
                                                                  AssemblyTitle,
                                                                  AssemblyVersion,
                                                                  AssemblyDescription,
                                                                  AssemblyProduct,
                                                                  AssemblyCopyright,
                                                                  AssemblyCompany);

        // Netchecking
        private static Task taskNetchecking;
        private static CancellationTokenSource taskCT;
        private static TcpClient tcpClient;
        private static int NetcheckRateDefault = 2000;
        public static int NetcheckRate = NetcheckRateDefault;
        public static int ReconnectedTimeOut = 5;
        public static int ReconnectedCount = 0;

        // update process
        private static Process updateApp;


        // OCX
        /// <summary>
        /// 注册表位置根目录
        /// </summary>
        private static string comRegistrySubKey = @"TypeLib\";
        /// <summary>
        /// COM ID
        /// </summary>
        private static string comLCID = @"{F64D1AA2-EA00-4CE8-AA30-270B7A068E90}";
        /// <summary>
        /// OCX路径
        /// </summary>
        private static string ocxSubKey = @"\1.0\0\win32";


        #region Font

        public static Font Size(this Font font, int size)
        {
            Font newFont = new Font(font.FontFamily, size, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
            return newFont;
        }

        #endregion


        #region Control



        #endregion


        #region Windows

        //定义无边框窗体Form  
        [DllImport("user32.dll")]//*********************拖动无窗体的控件  
        public static extern bool ReleaseCapture();
        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

        public const int WM_SYSCOMMAND = 0x0112;
        public const int SC_MOVE = 0xF010;
        public const int HTCAPTION = 0x0002;

        public static void MoveWindows(this WindowsBase win)
        {
            ReleaseCapture();
            SendMessage(win.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
        }

        #endregion


        #region ApplicationData

        public static List<string> GetResolutionList(string[] rsls, string maxRsl)
        {
            List<string> liRsl = new List<string>();
            ResolutionDescribe rd = (ResolutionDescribe)int.Parse(maxRsl);
            int length = 0;
            switch (rd)
            {
                case ResolutionDescribe.AUTO:
                    break;
                case ResolutionDescribe.LD:
                    length = rsls.Length - 4;
                    break;
                case ResolutionDescribe.SD:
                    length = rsls.Length - 3;
                    break;
                case ResolutionDescribe.HD:
                    length = rsls.Length - 2;
                    break;
                case ResolutionDescribe.FHD:
                    length = rsls.Length - 1;
                    break;
                default:
                    break;
            }

            for (int i = 0; i < length; i++)
            {
                liRsl.Add(rsls[i]);
            }
            if (rd > ResolutionDescribe.LD) liRsl.Add(rsls[rsls.Length - 1]);

            return liRsl;
        }

        #endregion


        #region Update Check

        public static void UpdateCheck(bool slient = true)
        {
            try
            {
                updateApp = Process.Start(@"lib\TVUpdate.exe", slient ? "/s" : "");
                updateApp.Disposed += (o, e) => { updateApp = null; };
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("更新程序加载失败");
                Console.WriteLine(ex.Message);
            }

        }

        #endregion


        #region Save Password

        public static string[] GetUserInfo()
        {
            string[] userinfo = null;
            try
            {
                if (File.Exists(userInfoFilePath))
                {
                    var data = File.ReadAllBytes(userInfoFilePath);
                    string sStr = Encoding.UTF8.GetString(data);
                    string oStr = DecryptDES(sStr, EnDeKey);
                    userinfo = oStr.Split('|');
                }
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("get userinfo error : " + ex.Message);
                Console.WriteLine("get userinfo error : " + ex.Message);
            }
            return userinfo;
        }

        public static void SaveUesrInfo(string account, string password)
        {
            try
            {
                string oStr = account + "|" + password;
                string EnStr = EncryptDES(oStr, EnDeKey);
                byte[] data = Encoding.UTF8.GetBytes(EnStr);
                File.WriteAllBytes(userInfoFilePath, data);
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("save userinfo error : " + ex.Message);
                Console.WriteLine("save userinfo error : " + ex.Message);
            }
        }
        public static void ClearUserInfo()
        {
            try
            {
                if (File.Exists(userInfoFilePath))
                {
                    File.Delete(userInfoFilePath);
                }
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("clear userinfo error : " + ex.Message);
                Console.WriteLine("clear userinfo error : " + ex.Message);
            }
        }

        #endregion


        #region 流量上报

        /// <summary>
        /// 上报流量
        /// </summary>
        /// <param name="videoFlow"></param>
        public static void UploadVideoFlow(VideoFlow videoFlow)
        {
            string _json = JsonConvert.SerializeObject(videoFlow);
            Console.WriteLine("Flow json: " + _json);
            HttpAPI.UploadDataCompleted += _UploadDataCompleted;
            HttpAPI.AddFlowAsync(_json);
        }
        private static void _UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
        {
            try
            {
                HttpAPI.UploadDataCompleted -= _UploadDataCompleted;
                string _json = Encoding.UTF8.GetString(e.Result);
                TVHttpResult _ret = JsonConvert.DeserializeObject<TVHttpResult>(_json);
                Console.WriteLine("Server : " + _json);
            }
            catch (Exception ex)
            {
                Console.WriteLine("UploadData error : " + ex.Message);
            }
        }

        #endregion


        #region Helper


        #region Code

        /// <summary>
        /// 生成验证码
        /// </summary>
        /// <returns></returns>
        public static string CheckCode()            //此方法生成  
        {
            int number;
            char code;
            string checkCode = String.Empty;        //声明变量存储随机生成的4位英文或数字  
            Random random = new Random();           //生成随机数  
            for (int i = 0; i < 4; i++)
            {
                number = random.Next();             //返回非负随机数  
                if (number % 2 == 0)                //判断数字是否为偶数  
                    code = (char)('0' + (char)(number % 10));
                else                                //如果不是偶数  
                    code = (char)('A' + (char)(number % 26));
                checkCode += " " + code.ToString(); //累加字符串  
            }
            return checkCode;                       //返回生成的字符串  
        }
        /// <summary>
        /// 生成验证码图片
        /// </summary>
        /// <param name="checkCode"></param>
        /// <param name="picCode"></param>
        public static void CodeImage(string checkCode, PictureBox picCode)
        {
            if (checkCode == null || checkCode.Trim() == String.Empty)
                return;
            System.Drawing.Bitmap image = new System.Drawing.Bitmap((int)Math.Ceiling((checkCode.Length * 10.0)), 32);
            Graphics g = Graphics.FromImage(image);     //创建Graphics对象  
            try
            {
                Random random = new Random();           //生成随机生成器  
                g.Clear(Color.FromArgb(244, 244, 244)); //清空图片背景色  
                for (int i = 0; i < 3; i++)             //画图片的背景噪音线  
                {
                    int x1 = random.Next(image.Width);
                    int x2 = random.Next(image.Width);
                    int y1 = random.Next(image.Height);
                    int y2 = random.Next(image.Height);
                    g.DrawLine(new Pen(Color.Peru), x1, y1, x2, y2);
                }
                Font font = new System.Drawing.Font("Tahoma", 14, (System.Drawing.FontStyle.Bold));
                g.DrawString(checkCode, font, new SolidBrush(Color.Black), 1, 4);
                for (int i = 0; i < 150; i++)           //画图片的前景噪音点  
                {
                    int x = random.Next(image.Width);
                    int y = random.Next(image.Height);
                    image.SetPixel(x, y, Color.FromArgb(random.Next()));
                }
                //画图片的边框线  
                // g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
                picCode.Width = image.Width;            //设置PictureBox的宽度  
                picCode.Height = image.Height;          //设置PictureBox的高度  
                picCode.BackgroundImage = image;        //设置PictureBox的背景图像  
            }
            catch (Exception ex)
            {
                Console.WriteLine("验证码图片错误:" + ex.Message);
                TVLog.WriteLine("验证码图片错误:" + ex.Message);
            }
        }

        #endregion


        public static long GetTimeSnap()
        {
            return (long)(DateTime.UtcNow - DT1970).TotalMilliseconds;
        }


        #region 加 / 解 密

        //默认密钥向量
        private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
        /// DES加密字符串        
        /// 待加密的字符串
        /// 加密密钥,要求为8位
        /// 加密成功返回加密后的字符串，失败返回源串 
        public static string EncryptDES(string encryptString, string encryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Convert.ToBase64String(mStream.ToArray());
            }
            catch
            {
                return encryptString;
            }
        }
        /// 
        /// DES解密字符串        
        /// 待解密的字符串
        /// 解密密钥,要求为8位,和加密密钥相同
        /// 解密成功返回解密后的字符串，失败返源串
        public static string DecryptDES(string decryptString, string decryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey);
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Convert.FromBase64String(decryptString);
                DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(mStream.ToArray());
            }
            catch
            {
                return decryptString;
            }
        }

        #endregion


        #region 程序自启

        public static void SetAutoRun(bool isAutoRun, bool currentUserOnly = true)
        {
            RegistryKey reg = null;
            string app = Application.ExecutablePath;
            if (!System.IO.File.Exists(app))
                throw new Exception("该文件不存在!");
            String name = app.Substring(app.LastIndexOf(@"/") + 1);
            if (currentUserOnly)
                reg = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
            else
                reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);

            if (reg == null)
                if (currentUserOnly)
                    reg = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
                else
                    reg = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run");

            if (isAutoRun)
                reg.SetValue(name, app);
            else
                reg.SetValue(name, false);

            Console.WriteLine("设置开机启动:" + isAutoRun);

            if (reg != null)
                reg.Close();
        }

        #endregion


        #region 启动检测

        public static bool StartupCheck()
        {


            bool ready = true;
#if !DEBUG
            ready = CommonFun.CheckCOM();
            if (!ready)
            {
                TVLog.WriteLine("组件初始化失败。");
                Windows_MessageBox wm = new Windows_MessageBox(TVString.STR_OcxError, MessageBoxButtons.OK, MessageBoxIcon.Error);
                wm.StartPosition = FormStartPosition.CenterScreen;
                wm.SetConfirmText(TVString.STR_Quit);
                wm.ShowDialog();
            }
#endif
            if (ready)
            {
                ready = CommonFun.ProcessCheck();
            }
            return ready;
        }

        #endregion


        #region 线程检测

        public static bool ProcessCheck()
        {
            bool ok = true;
            try
            {
                Process curProcess = Process.GetCurrentProcess();

                List<Process> pro = Process.GetProcessesByName(curProcess.ProcessName.Replace(".vshost", "")).Where(o => o.Id != curProcess.Id).ToList();

                if (pro.Count > 0)
                {
                    TVLog.WriteLine("程序已运行，转到程序。");
                    Console.WriteLine("程序已运行，转到程序。");
                    //if (MessageBox.Show("当前程序正在运行，更新将会退出程序。\r\n是否继续?", "提示", MessageBoxButtons.OKCancel) == DialogResult.OK)
                    //{
                    pro.ForEach(o =>
                    {
                        Console.WriteLine(o.MainWindowTitle);
                        IntPtr hwnd = User32.FindWindow("远程协作");
                        User32.ShowWindows(hwnd);
                    });

                    ok = false;
                    //}
                    //else
                    //{
                    //    Console.WriteLine("退出程序");

                    //    Application.Exit();

                    //    curProcess.Kill();

                    //ok = false;
                    //}
                }
            }
            catch (Exception ex)
            {
                ok = false;
            }

            return ok;
        }

        #endregion


        #region ocx控件检测

        //===>COM
        //计算机\HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{F64D1AA2-EA00-4CE8-AA30-270B7A068E90}\1.0\0\win32
        public static bool CheckCOM()
        {
            TVLog.WriteLine("==>检测注册的COM组件");

            try
            {
                using (RegistryKey comSubKey = Registry.ClassesRoot.OpenSubKey(comRegistrySubKey + comLCID))
                {
                    if (comSubKey == null)
                        return false;

                    using (RegistryKey ocxPathSubKey = Registry.ClassesRoot.OpenSubKey(comRegistrySubKey + comLCID + ocxSubKey))
                    {
                        if (ocxPathSubKey == null)
                            return false;

                        string comOCXPath = ocxPathSubKey.GetValue("") as string;
                        TVLog.WriteLine(">>>>>>>>>>>>> " + comOCXPath);

                        //string comOCXDictory = comOCXPath.Substring(0, comOCXPath.LastIndexOf(@"\") + 1);
                        //comOCXDictory = comOCXDictory.ToLower();
                        comOCXPath = comOCXPath.ToLower();

                        string app = Application.StartupPath;

                        //if (!System.IO.File.Exists(app))
                        //    throw new Exception("File does not exist");
                        //String appDictory = app.Substring(0, app.LastIndexOf(@"\") + 1);
                        string appDictory = app + @"\bin\zwanphone.ocx";
                        appDictory = appDictory.ToLower();


                        TVLog.WriteLine(string.Format("检测COM组件:\r\n{0}\r\n{1}", comOCXPath, appDictory));

                        return comOCXPath.Equals(appDictory);
                    }
                }
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("检测COM组件失败:" + ex.Message);

                return false;
            }
        }

        #endregion


        #region 网络检测

        public static async void Netchecking(int? port, int? millisecondsTimeout)
        {
            if (taskNetchecking != null)
            {
                taskCT.Cancel();
                while (!taskNetchecking.IsCompleted & !taskNetchecking.IsCanceled)
                {
                    await Task.Delay(100);
                }
                taskNetchecking.Dispose();
                taskNetchecking = null;
            }
            taskCT = new CancellationTokenSource();
            taskNetchecking = Task.Factory.StartNew(() =>
            {
                try
                {
                    while (!taskCT.Token.IsCancellationRequested)
                    {
                        tcpClient = TcpClientConnector.Connect(Host, port, millisecondsTimeout);
                        if (tcpClient != null)
                        {
                            tcpClient.GetStream().Close();
                            tcpClient.Close();
                            IsHostAlive = true;
                            NetcheckRate = NetcheckRateDefault;
                            ReconnectedCount = 0;
                        }
                        else
                        {
                            ReconnectedCount++;
                            if (ReconnectedTimeOut == ReconnectedCount)
                                IsHostAlive = false;
                        }
                        taskNetchecking.Wait(NetcheckRate);
                    }
                }
                catch (Exception ex)
                {
                    //TVLog.WriteLine("net checking error : " + ex.Message);
                    NetcheckRate = NetcheckRateDefault * 2;
                }
            }, taskCT.Token);
        }

        public static async Task NetcheckStop()
        {
            taskCT.Cancel();
            while (!taskNetchecking.IsCompleted & !taskNetchecking.IsCanceled)
            {
                await Task.Delay(17);
            }
        }

        #endregion


        #region 更新程序检测

        public static void CloseUpdateApp()
        {
            try
            {
                if (updateApp != null)
                {
                    updateApp.Kill();
                }
            }
            catch (Exception ex)
            {
                TVLog.WriteLine("更新程序释放错误:" + ex.Message);
            }
        }


        #endregion


        #endregion




    }

    /// <summary>
    /// 上传文件信息
    /// </summary>
    public class UploadInfo : IDisposable
    {
        public string FileFullName;

        private FileInfo fileInfo;
        public FileInfo _FileInfo
        {
            get
            {
                if (fileInfo == null)
                    fileInfo = new FileInfo(FileFullName);
                return fileInfo;
            }
        }

        public string FileName
        {
            get
            {
                return _FileInfo.Name;
            }
        }
        public long FileSize
        {
            get
            {
                return _FileInfo.Length;
            }
        }

        public long BytesSend;
        public long BytesReceived;
        public int ProgressPercentage;
        public long TotalBytesToSend;
        public long TotalBytesToReceive;

        public string data;

        public delegate void OnCompletedHandler(UploadInfo sender);
        public delegate void OnProgressingHandler(UploadInfo sender);

        public event OnProgressingHandler OnProgressing;
        public event OnCompletedHandler OnCompleted;

        private string tmpFile;

        private WebClient _webClient;

        public UploadInfo(string fileFullName)
        {
            if (!File.Exists(fileFullName))
                throw new Exception("file " + fileFullName + " not found");

            FileFullName = fileFullName;

            HttpAPI.UploadProgressChanged += HttpAPI_UploadProgressChanged1;
            HttpAPI.UploadFileCompleted += HttpAPI_UploadFileCompleted1;
        }

        public string Start()
        {
            HttpAPI.UploadFile(FileFullName, ApplicationData.Account);

            return FileFullName;
        }

        public void Stop()
        {
            if (_webClient.IsBusy)
                _webClient?.CancelAsync();
            else
                _webClient.Dispose();
        }

        private void HttpAPI_UploadFileCompleted1(object sender, UploadFileCompletedEventArgs e)
        {
            _webClient = (WebClient)sender;
            string _json = Encoding.UTF8.GetString(e.Result);
            data = JsonConvert.DeserializeObject<TVHttpResult>(_json)?.data;

            if (OnCompleted != null)
                OnCompleted.Invoke(this);
        }

        private void HttpAPI_UploadProgressChanged1(object sender, UploadProgressChangedEventArgs e)
        {
            _webClient = sender as WebClient;
            this.BytesSend = e.BytesSent;
            this.BytesReceived = e.BytesReceived;
            this.TotalBytesToSend = e.TotalBytesToSend;
            this.TotalBytesToReceive = e.TotalBytesToReceive;
            this.ProgressPercentage = e.ProgressPercentage;

            if (OnProgressing != null)
                OnProgressing.Invoke(this);
        }

        public void Dispose()
        {
            HttpAPI.UploadProgressChanged -= HttpAPI_UploadProgressChanged1;
            HttpAPI.UploadFileCompleted -= HttpAPI_UploadFileCompleted1;
        }
    }
}
